﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Text.RegularExpressions;

namespace Pinwheel.LSerialize.Formatters
{
    [Preserve]
    public sealed unsafe partial class TypeFormatter : LFormatter<Type>
    {
        // Remove Version, Culture, PublicKeyToken from AssemblyQualifiedName.
        // Result will be "TypeName, Assembly"
        // see:http://msdn.microsoft.com/en-us/library/w3f99sx1.aspx
        
#if NET7_0_OR_GREATER
        [GeneratedRegex(@", Version=\d+.\d+.\d+.\d+, Culture=[\w-]+, PublicKeyToken=(?:null|[a-f0-9]{16})")]
        private static partial Regex ShortTypeNameRegex();
#else
        static readonly Regex _shortTypeNameRegex = new Regex(@", Version=\d+.\d+.\d+.\d+, Culture=[\w-]+, PublicKeyToken=(?:null|[a-f0-9]{16})", RegexOptions.Compiled);
        static Regex ShortTypeNameRegex() => _shortTypeNameRegex;
#endif
        
        // private static ConcurrentDictionary<Type, string> m_TypeToName = new ConcurrentDictionary<Type, string>();
        // private static ConcurrentDictionary<string, Type> m_NameToType = new ConcurrentDictionary<string, Type>();
        
        [ThreadStatic]
        private static Dictionary<Type, string> m_TypeToName;
        [ThreadStatic]
        private static Dictionary<string, Type> m_NameToType;
        static TypeFormatter()
        {
            m_TypeToName = new Dictionary<Type, string>();
            m_NameToType = new Dictionary<string, Type>();
        }

        [Preserve]
        public override void Serialize(LSerializeContext* context, LStreamWriter* writer, ref Type value)
        {
            Profiler.BeginSample("TypeFormatter.Serialize");
            if (!m_TypeToName.TryGetValue(value, out var shortName))
            {
                shortName = ShortTypeNameRegex().Replace(value.AssemblyQualifiedName, "");
                m_TypeToName.TryAdd(value, shortName);
                m_NameToType.TryAdd(shortName, value);
            }
            writer->WriteString(shortName);
            Profiler.EndSample();
        }

        [Preserve]
        public override void Deserialize(LSerializeContext* context, LStreamReader* reader, ref Type value)
        {
            Profiler.BeginSample("TypeFormatter.Deserialize");
            var typeName = reader->ReadString();
            if (!m_NameToType.TryGetValue(typeName, out value))
            {
                value = Type.GetType(typeName, throwOnError: true);
                m_TypeToName.TryAdd(value, typeName);
                m_NameToType.TryAdd(typeName, value);
            }
            Profiler.EndSample();
        }
    } 
}


